package iris_plugin

import (
	"encoding/json"
	"fmt"
	"gitee.com/kristas/booting-go/framework/core/log"
	"gitee.com/kristas/booting-go/framework/core/statement/types"
	"gitee.com/kristas/booting-go/framework/web"
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/context"
	"strings"
)

type Starter struct {
	types.Component
	app        *iris.Application
	Log        log.Logger         `wire:""`
	IrisConfig iris.Configuration `value:"iris"`
	LogConf    *log.Configure
}

func NewStarter() *Starter {
	return &Starter{}
}

func (r *Starter) Init() error {
	r.app = iris.New()
	r.app.Logger().SetLevel(irisLevelAdapter(r.LogConf.Level))
	return nil
}

func (r *Starter) RegisterEndpoint(endpoint web.Endpoint) {
	info := endpoint.GetApiInfo()
	r.app.Handle(info.GetHttpMethod(), info.GetURL(), func(ctx context.Context) {
		endpoint.Handler(newParamAdapter(ctx), func(result interface{}) error {
			return negotiationResult(ctx, result)
		})
	})
}

func (r *Starter) FsHandler(prefix, dir string) {
	r.app.HandleDir(prefix, dir)
}

func (r *Starter) ListenAndServe(port int) error {
	return r.app.Listen(fmt.Sprintf(":%d", port),
		iris.WithConfiguration(r.IrisConfig),
	)
}

func irisLevelAdapter(level string) string {
	switch strings.ToLower(level) {
	case "panic":
		return "fatal"
	case "trace":
		return "debug"
	default:
		return level
	}
}

func negotiationResult(c context.Context, r interface{}) error {
	accept := c.GetHeader("Accept")
	switch accept {
	case "*/*", "":
		_, err := c.JSON(r)
		return err
	default:
		bytes, _ := json.Marshal(r)
		c.Negotiation().
			JSON(r).
			XML(r).
			YAML(r).
			HTML(string(bytes)).
			Any(string(bytes))
		_, err := c.Negotiate(nil)
		return err
	}
}
